/*
 * Copyright (c) 2023-2024 elsfs Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.elsfs.cloud.screw.process;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import org.elsfs.cloud.common.util.lang.Assert;
import org.elsfs.cloud.common.util.lang.BeanUtils;
import org.elsfs.cloud.common.util.lang.CollectionUtils;
import org.elsfs.cloud.screw.Configuration;
import org.elsfs.cloud.screw.engine.EngineFileType;
import org.elsfs.cloud.screw.metadata.Column;
import org.elsfs.cloud.screw.metadata.PrimaryKey;
import org.elsfs.cloud.screw.metadata.Table;
import org.elsfs.cloud.screw.metadata.model.ColumnModel;
import org.elsfs.cloud.screw.metadata.model.DataModel;
import org.elsfs.cloud.screw.metadata.model.TableModel;

/**
 * AbstractBuilder
 *
 * @author zeng
 */
public abstract class AbstractProcess implements Process {

  /** 表信息缓存 */
  volatile Map<String, List<? extends Table>> tablesCaching = new ConcurrentHashMap<>();

  /** 列信息缓存 */
  volatile Map<String, List<Column>> columnsCaching = new ConcurrentHashMap<>();

  /** 主键信息缓存 */
  volatile Map<String, List<PrimaryKey>> primaryKeysCaching = new ConcurrentHashMap<>();

  /** Configuration */
  protected Configuration config;

  /** 构造方法 */
  private AbstractProcess() {}

  /**
   * 构造方法
   *
   * @param configuration {@link Configuration}
   */
  protected AbstractProcess(Configuration configuration) {
    Assert.notNull(configuration, "Configuration can not be empty!");
    this.config = configuration;
  }

  /**
   * 过滤表 存在指定生成和指定不生成，优先级为：如果指定生成，只会生成指定的表，未指定的不会生成，也不会处理忽略表
   *
   * @param tables {@link List} 处理前数据
   * @return {@link List} 处理过后的数据
   * @see "1.0.3"
   */
  protected List<TableModel> filterTables(List<TableModel> tables) {
    ProcessConfig produceConfig = config.getProduceConfig();
    if (!Objects.isNull(config) && !Objects.isNull(config.getProduceConfig())) {
      // 指定生成的表名、前缀、后缀任意不为空，按照指定表生成，其余不生成，不会在处理忽略表
      if (CollectionUtils.isNotEmpty(produceConfig.getDesignatedTableName())
          // 前缀
          || CollectionUtils.isNotEmpty(produceConfig.getDesignatedTablePrefix())
          // 后缀
          || CollectionUtils.isNotEmpty(produceConfig.getDesignatedTableSuffix())) {
        return handleDesignated(tables);
      } else {
        // 处理忽略表
        return handleIgnore(tables);
      }
    }
    return tables;
  }

  /**
   * 处理指定表
   *
   * @param tables {@link List} 处理前数据
   * @return {@link List} 处理过后的数据
   * @see "1.0.3"
   */
  private List<TableModel> handleDesignated(List<TableModel> tables) {
    List<TableModel> tableModels = new ArrayList<>();
    ProcessConfig produceConfig = this.config.getProduceConfig();
    if (!Objects.isNull(produceConfig)) {
      // 指定表名
      if (CollectionUtils.isNotEmpty(produceConfig.getDesignatedTableName())) {
        List<String> list = produceConfig.getDesignatedTableName();
        for (String name : list) {
          tableModels.addAll(tables.stream().filter(j -> j.getTableName().equals(name)).toList());
        }
      }
      // 指定表名前缀
      if (CollectionUtils.isNotEmpty(produceConfig.getDesignatedTablePrefix())) {
        List<String> list = produceConfig.getDesignatedTablePrefix();
        for (String prefix : list) {
          tableModels.addAll(
              tables.stream().filter(j -> j.getTableName().startsWith(prefix)).toList());
        }
      }
      // 指定表名后缀
      if (CollectionUtils.isNotEmpty(produceConfig.getDesignatedTableSuffix())) {
        List<String> list = produceConfig.getDesignatedTableSuffix();
        for (String suffix : list) {
          tableModels.addAll(
              tables.stream().filter(j -> j.getTableName().endsWith(suffix)).toList());
        }
      }
      return tableModels;
    }
    return tableModels;
  }

  /**
   * 处理忽略
   *
   * @param tables {@link List} 处理前数据
   * @return {@link List} 处理过后的数据
   */
  private List<TableModel> handleIgnore(List<TableModel> tables) {
    ProcessConfig produceConfig = config.getProduceConfig();
    if (!Objects.isNull(produceConfig)) {
      // 处理忽略表名
      if (CollectionUtils.isNotEmpty(produceConfig.getIgnoreTableName())) {
        List<String> list = produceConfig.getIgnoreTableName();
        for (String name : list) {
          tables =
              tables.stream()
                  .filter(j -> !j.getTableName().equals(name))
                  .collect(Collectors.toList());
        }
      }
      // 忽略表名前缀
      if (CollectionUtils.isNotEmpty(produceConfig.getIgnoreTablePrefix())) {
        List<String> list = produceConfig.getIgnoreTablePrefix();
        for (String prefix : list) {
          tables =
              tables.stream()
                  .filter(j -> !j.getTableName().startsWith(prefix))
                  .collect(Collectors.toList());
        }
      }
      // 忽略表名后缀
      if (CollectionUtils.isNotEmpty(produceConfig.getIgnoreTableSuffix())) {
        List<String> list = produceConfig.getIgnoreTableSuffix();
        for (String suffix : list) {
          tables =
              tables.stream()
                  .filter(j -> !j.getTableName().endsWith(suffix))
                  .collect(Collectors.toList());
        }
      }
      return tables;
    }
    return tables;
  }

  /**
   * 优化数据
   *
   * @param dataModel {@link DataModel}
   * @see "1.0.3"
   */
  public void optimizeData(DataModel dataModel) {
    // trim
    BeanUtils.beanAttributeValueTrim(dataModel);
    // tables
    List<TableModel> tables = dataModel.getTables();
    // columns
    tables.forEach(
        i -> {
          // table escape xml
          BeanUtils.beanAttributeValueTrim(i);
          List<ColumnModel> columns = i.getColumns();
          // columns escape xml
          columns.forEach(BeanUtils::beanAttributeValueTrim);
        });
    // if file type is word
    if (config.getEngineConfig().getFileType().equals(EngineFileType.WORD)) {
      // escape xml
      BeanUtils.beanAttributeValueEscapeXml(dataModel);
      // tables
      tables.forEach(
          i -> {
            // table escape xml
            BeanUtils.beanAttributeValueEscapeXml(i);
            List<ColumnModel> columns = i.getColumns();
            // columns escape xml
            columns.forEach(BeanUtils::beanAttributeValueEscapeXml);
          });
    }
    // if file type is markdown
    if (config.getEngineConfig().getFileType().equals(EngineFileType.MD)) {
      // escape xml
      BeanUtils.beanAttributeValueReplaceBlank(dataModel);
      // columns
      tables.forEach(
          i -> {
            // table escape xml
            BeanUtils.beanAttributeValueReplaceBlank(i);
            List<ColumnModel> columns = i.getColumns();
            // columns escape xml
            columns.forEach(BeanUtils::beanAttributeValueReplaceBlank);
          });
    }
  }
}
